package authentication

import (
	"crypto/md5"
	"errors"
	"fmt"
	"gitee.com/Luna-CY/hui-hui/internal/cache"
	"gitee.com/Luna-CY/hui-hui/internal/configure"
	"gitee.com/Luna-CY/hui-hui/internal/logger"
	"gitee.com/Luna-CY/hui-hui/internal/runtime"
	"gitee.com/Luna-CY/hui-hui/server/http/response"
	"github.com/gin-gonic/gin"
	"github.com/golang-jwt/jwt/v5"
	"github.com/pquerna/otp/totp"
	"math/rand"
	"strconv"
	"time"
)

type LoginRequest struct {
	Code string `json:"code" validate:"required" minimum:"6" maximum:"32" binding:"required,min=6,max=32"` // 口令
}

type LoginResponse struct {
	Token string `json:"token" validate:"required"` // JWT Token
}

func (cls *Authentication) Login(c *gin.Context) (int, any, error) {
	var body = LoginRequest{}
	if err := c.ShouldBindJSON(&body); nil != err {
		return response.InvalidRequest, nil, errors.New("无效请求")
	}

	// 验证
	if code, content, err := cls.verify(&body); response.Ok != code || nil != err {
		return code, content, err
	}

	// 生成Token
	var secret = strconv.FormatFloat(rand.Float64(), 'E', -1, 64)
	if err := cache.GetCache().Set(cache.AuthorizationSecret, secret, 3600*time.Second); nil != err {
		return response.Unknown, nil, errors.New("内部错误")
	}

	token, err := jwt.NewWithClaims(jwt.SigningMethodHS512, jwt.MapClaims{"secret": secret}).SignedString([]byte(runtime.Uuid))
	if nil != err {
		logger.GetLogger().Sugar().Errorf("生成JWT Token失败: %s", err)

		return response.Unknown, nil, errors.New("内部错误")
	}

	return response.Ok, LoginResponse{Token: token}, nil
}

// verify 根据配置的验证方式验证口令
func (cls *Authentication) verify(body *LoginRequest) (int, any, error) {
	switch configure.Configure.Server.Authentication.Type {
	case configure.AuthenticationTypeFixed:
		if fmt.Sprintf("%x", md5.Sum([]byte(body.Code))) != configure.Configure.Server.Authentication.Password {
			return response.AuthorizeLoginUsernameOrPasswordInvalid, nil, errors.New("口令错误")
		}
	case configure.AuthenticationTypeEmailCode:
		code, err := cache.GetCache().Get(cache.AuthorizationCode)
		if nil != err {
			return response.Unknown, nil, errors.New(err.Error())
		}

		if body.Code != code {
			return response.AuthorizeLoginUsernameOrPasswordInvalid, nil, errors.New("口令错误")
		}

		// 删除验证码
		if err := cache.GetCache().Del(cache.AuthorizationCode); nil != err {
			return response.Unknown, nil, err
		}
	case configure.AuthenticationTypeTOTP:
		if !totp.Validate(body.Code, configure.Configure.Server.Authentication.Secret) {
			return response.AuthorizeLoginUsernameOrPasswordInvalid, nil, errors.New("口令错误")
		}
	default:
		return response.AuthorizeLoginUsernameOrPasswordInvalid, nil, errors.New("口令错误")
	}

	return response.Ok, nil, nil
}
